/******************************************************************************
Copyright (c) 2013-2014, Altera Corporation.  All rights reserved.

Redistribution and use of this software, in source and binary code forms, with or without modification, are permitted provided that the foll
owing conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the doc
umentation and/or other materials provided with the distribution.

3. Neither the name of Altera Corporation nor the names of its contributors may be used to endorse or promote products derived from this sof
tware without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITE
D TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF L
IABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTW
ARE, EVEN IF THE COPYRIGHT HOLDERS OR CONTRIBUTORS ARE ADVISED OF THE POSSIBILITY DAMAGE IS LIKELY TO OCCUR.
******************************************************************************/
#include <stdio.h>
#include "core.h"
#include "scu.h"
#include "cache.h"
#include "set_vbar.h"
#include "Layout.h"
#include "mem.h"
#include "ipc.h"

#define WAY_SIZE (64*1024)
// Were going to assume that this all loads in 1 way
uint32 *p_LL = (uint32 *) 0xFFFF0010;

/**********************************************************

    Support Functions    

**********************************************************/
uint32 g_abort_count;
void cache_abort_handler(void)
{
  g_abort_count++;
}
#define Cached_Memory    l2_context_Cache_Test_MR_1_MapTo
#define Physical_Memory l2_context_Cache_Test_MR_MapTo
#define p_Cached_Memory    ((char *)Cached_Memory)
#define p_Physical_Memory ((char *)Physical_Memory)

/**********************************************************

    Test Functions    

**********************************************************/
uint32 test_l2_cache(void)
{
  uint32 retval = 0;
  char *incorrectmem;

  g_abort_count = 0;
  // in case of abort we want to catch it and report an error
  setupInts(NULL, cache_abort_handler, NULL, NULL);
  setTestVbar();

 // enable l2 cache
  alt_cache_l2_init();
  alt_cache_l2_prefetch_enable();
  alt_cache_l2_parity_enable();
  alt_cache_l2_enable();

  Map_l2_context();

// Step 1 - Check alt_cache_l2_load2ways
  alt_cache_l2_load2ways(p_Cached_Memory, WAY_SIZE, WAY0);

  // Write to cachable memory
  memset(p_Cached_Memory, 0xDD, WAY_SIZE);

  // Write to physical memory
  memset(p_Physical_Memory, 0xAA, WAY_SIZE);
  // Cached memory should not be effected

  incorrectmem = memcheck(p_Cached_Memory, 0xDD, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("1. First non-cached mem = %p\n", incorrectmem);
    retval |= 1<<0;
  }
// Step 2 - Test alt_cache_l2_clean_cache_by_way
  alt_cache_l1_data_clean(p_Cached_Memory, WAY_SIZE);
  alt_cache_l2_clean_cache_by_way(WAY0);

  // Data Cache should be written to physical
  incorrectmem = memcheck(p_Physical_Memory, 0xDD, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("2. First incorrect physical mem = %p\n", incorrectmem);
    retval |= 1<<1;
  }
  memset(p_Physical_Memory, 0xAA, WAY_SIZE);

  incorrectmem = memcheck(p_Cached_Memory, 0xDD, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("3. First non-cached mem = %p\n", incorrectmem);
    retval |= 1<<2;
  }

// Step 3 - Test Clean and invalidate
  // Need to re-write to dirty it up
  memset(p_Cached_Memory, 0x55, WAY_SIZE);
  alt_cache_l1_data_clean(p_Cached_Memory, WAY_SIZE);
  //alt_cache_l2_clean_invalidate_cache_by_way(WAY0);
  alt_cache_l2_clean_cache_by_way(WAY0);

  // Did the clean part work?
  incorrectmem = memcheck(p_Physical_Memory, 0x55, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("4. First incorrect physical mem = %p\n", incorrectmem);
    retval |= 1<<4;
  }

  // Did the invalidate part work?
  memset(p_Physical_Memory, 0x77, WAY_SIZE);
  alt_cache_l1_data_invalidate(p_Cached_Memory, WAY_SIZE);
  incorrectmem = memcheck(p_Cached_Memory, 0x77, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("5. First non-cached mem = %p\n", incorrectmem);
    retval |= 1<<3;
  }
// Step 4 - Test invalidate
  alt_cache_l1_data_invalidate(p_Cached_Memory, WAY_SIZE);
  alt_cache_l2_invalidate_cache_by_way(WAY0);
  memset(p_Physical_Memory, 0x66, WAY_SIZE);
  
  // Should be reading physical mem through the cache
  incorrectmem = memcheck(p_Cached_Memory, 0x66, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    uint32 count;
    printf("6. First incorrect physical mem = %p\n", incorrectmem);
    retval |= 1<<3;
    for(count=WAY1; count <= WAY7; count <<= 1)
    {
      alt_cache_l2_clean_cache_by_way(count);
      if(NULL == memcheck(p_Cached_Memory, 0xAA, WAY_SIZE))
      {
        printf("Last way = %x\n", count);
      }
    }
  }
  alt_cache_l2_set_locks(CPU0|CPU1, WAY0_7);
  alt_cache_l2_clean_cache_by_way(WAY0_7);
  alt_cache_l2_disable();
  alt_cache_l1_data_clean_all();
  alt_mmu_disable2();

p_LL[0] = p_LL[1] = p_LL[2] = p_LL[3] = 0;
  return retval;
}


/**********************************************************

    Support Functions    

**********************************************************/

uint32 get_char_count(char *Memory, char character, uint32 size)
{
  int count, retval = 0;
  for(count=0; count < size; count++)
  {
    if(Memory[count] == character)
      retval++;
  }
  return retval;
}
uint32 retval1 = 0;

/**********************************************************

    Test Functions    

**********************************************************/

uint32 test_l2_cache_cpu0(void)
{
  uint32 retval = 0;
  uint32 count = 0, count2 = 0;
  char *incorrectmem;
  retval1 = 0;

p_LL[0]|=1<<0;
// Need to disable l1
 // enable l2 cache
  alt_cache_l2_init();
  alt_cache_l2_prefetch_enable();
  alt_cache_l2_parity_enable();
  alt_cache_l2_invalidate_cache_by_way(WAY0_7);
  alt_cache_l1_data_invalidate_all();
  alt_cache_l2_enable();
p_LL[0]|=1<<1;

  Map_l2_context();

//  - check alt_cache_l2_set_locks
 // Part A - check No Data written if locked
  // Lock all of cached memory against new data, writable by instructions
  alt_cache_l2_set_locks(CPU0_DATA, WAY0_7);
p_LL[0]|=1<<2;

  // Clean the whole cache
  alt_cache_l2_invalidate_cache_by_way(WAY0_7);

  // Check that data won't write
  memset(p_Cached_Memory, 0xCC, WAY_SIZE);
  alt_cache_l1_data_clean(p_Cached_Memory, WAY_SIZE);

p_LL[0]|=1<<3;
  incorrectmem = memcheck(p_Physical_Memory, 0xCC, WAY_SIZE);
  if(incorrectmem != NULL)
  {
    printf("Data incorrectly cached = %p\n", incorrectmem);
    retval |= 1<<0;
  }

 // Part B - Check Data limited to given ways
  // Allow data writes to some caches
  alt_cache_l2_set_locks(CPU0_DATA, WAY0_3|WAY4|WAY5);
p_LL[0]|=1<<4;

  // Load up some memory
  memset(p_Cached_Memory, 0xEE, WAY_SIZE);

  // Lock the given ones (so they don't get flushed)
  alt_cache_l2_set_locks(CPU0_DATA, WAY0_7);
  count = get_char_count(p_Cached_Memory, 0xEE, WAY_SIZE);
p_LL[0]|=1<<5;
  // Write to Physical
  memset(p_Physical_Memory, 0x99, WAY_SIZE);
  // Clean all caches except the given ones
  alt_cache_l2_clean_cache_by_way(WAY0_3|WAY4|WAY5);
p_LL[0]|=1<<6;

  // Check to make sure that it didn't become uncached
  count2 = get_char_count(p_Cached_Memory, 0xEE, WAY_SIZE);
  if(count != count2)
    retval |= 1<<1; 
p_LL[0]|=1<<7;

  // Part C - Allow our CPU access to data, but not other CPU
  alt_cache_l2_set_locks(CPU0_DATA, WAY0_3|WAY6|WAY7);
  alt_cache_l2_set_locks(CPU0_INSTRUCTION | CPU1 , WAY0_7);

p_LL[0]|=1<<8;
  // We're not testing the SCU yet
  alt_cache_l1_data_clean_all();

  SetState(IPC_CACHE_TESTS + 1);
p_LL[0]|=1<<9;
  // Other cpu runs its tests, sets retval1
  WaitForState(IPC_CACHE_TESTS + 2);

  if(g_abort_count != 0)
    retval |= 1u<<31;
p_LL[0]|=1<<10;

  alt_cache_l2_set_locks(CPU0|CPU1, WAY0_7);
  alt_cache_l2_clean_cache_by_way(WAY0_7);
  alt_cache_l2_disable();
p_LL[0]|=1<<11;

  alt_cache_l1_data_clean_all();
  SetState(IPC_CACHE_TESTS + 3);

  alt_mmu_disable2();
p_LL[0]|=1<<12;
  if(retval | retval1)
    printf("retval=%x,retval1=%x\n", retval, retval1);
p_LL[0]|=1<<13;
  return retval | retval1;
}




void test_l2_cache_cpu1(void)
{
  // Wait for CPU0 to be ready
p_LL[8]=0x1234;
p_LL[1]|=1<<0;
  WaitForState(IPC_CACHE_TESTS + 1);
p_LL[1]|=1<<1;
  setTestVbar();
p_LL[1]|=1<<2;
  alt_cache_l1_data_invalidate_all();
  Map_l2_context();
p_LL[1]|=1<<3;

  // CPU1 can't add new entries, but could reuse old ones
  alt_cache_l2_clean_invalidate_cache_by_way(WAY0_7);
p_LL[1]|=1<<4;

  // Write to memory to attempt to load to cache
  memset(p_Cached_Memory, 0x33, 2*WAY_SIZE);
p_LL[1]|=1<<5;
  memset(p_Physical_Memory, 0x22, 2*WAY_SIZE);
p_LL[1]|=1<<6;

  // Check to make sure that it isn't being cached
  if(NULL != memcheck(p_Cached_Memory, 0x22, 2*WAY_SIZE))
    retval1 |= 1<<2;
p_LL[1]|=1<<7;

  alt_cache_l1_data_clean_all();

p_LL[1]|=1<<8;
  SetState(IPC_CACHE_TESTS + 2);

p_LL[1]|=1<<9;
  WaitForState(IPC_CACHE_TESTS + 3);
  alt_mmu_disable2();

p_LL[1]|=1<<10;
}

/**********************************************************

    SCU Functions    

**********************************************************/
#define SCU_MEM_SIZE    64
#define SCU_MEM_OFFSET  128

#define p_scu_Cached_Memory    ((char *)Cached_Memory + SCU_MEM_OFFSET)
#define p_scu_Physical_Memory ((char *)Physical_Memory + SCU_MEM_OFFSET)

uint32 test_scu_cpu0(void)
{
  uint32 retval = 0;
p_LL[2]|=1<<0;

// Test 1 - With SCU on, check for success
  enable_scu();
p_LL[2]|=1<<1;

p_LL[2]|=1<<2;
  alt_cache_l2_invalidate_cache_by_way(WAY0_7);
p_LL[2]|=1<<3;
  alt_cache_l2_enable();
p_LL[2]|=1<<4;
  alt_cache_l1_data_invalidate_all();
p_LL[2]|=1<<5;
  Map_l2_context();
p_LL[2]|=1<<6;
  alt_cache_l1_enable_all();
p_LL[2]|=1<<7;
  memset(p_scu_Physical_Memory, 0x33, SCU_MEM_SIZE);
p_LL[2]|=1<<8;
  memset(p_scu_Cached_Memory, 0x44, SCU_MEM_SIZE);
p_LL[2]|=1<<9;
  // Wait for CPU1 to check
  SetState(IPC_SCU_TESTS + 3);
p_LL[2]|=1<<10;
  WaitForState(IPC_SCU_TESTS + 4);
p_LL[2]|=1<<11;

// Test 2 - Check for CPU1's L1
  if(NULL != memcheck(p_scu_Cached_Memory, 0x55, SCU_MEM_SIZE))
    retval |= 4;
p_LL[2]|=1<<12;
  alt_cache_l1_data_clean_all();
p_LL[2]|=1<<13;
  alt_cache_l2_clean_cache_by_way(WAY0_7);
  alt_cache_l2_disable();
p_LL[2]|=1<<14;
  SetState(IPC_SCU_TESTS + 5);

  alt_mmu_disable2();
p_LL[2]|=1<<15;
  return retval | retval1;
}


void test_scu_cpu1(void)
{
  retval1 = 0;

// Test 1
p_LL[3]|=1<<0;
  WaitForState(IPC_SCU_TESTS + 3);
p_LL[3]|=1<<1;
  alt_cache_l1_enable_all();
p_LL[3]|=1<<2;
  Map_l2_context();
p_LL[3]|=1<<3;
  if(NULL != memcheck(p_scu_Cached_Memory, 0x44, SCU_MEM_SIZE))
    retval1 |= 2;
p_LL[3]|=1<<4;

// Test 2
  memset(p_scu_Cached_Memory, 0x55, SCU_MEM_SIZE);
p_LL[3]|=1<<5;
  alt_cache_l1_data_clean_all();// Jumping to here
p_LL[3]|=1<<6;
  alt_mmu_disable2();
p_LL[3]|=1<<7;
  SetState(IPC_SCU_TESTS + 4);

  WaitForState(IPC_SCU_TESTS + 5);
  alt_mmu_disable2();
p_LL[3]|=1<<8;
}


